package cn.edu.hit;

import cn.edu.hit.kg.Neo4jDataProvider;
import cn.edu.hit.kg.data.GraphData;
import cn.edu.hit.kg.data.Node;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.cli.*;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        args = new String[]{"-h"};
        Options options = new Options();
//        options.addOption(org.apache.commons.cli.Option.builder("p")
//            .longOpt("primary-db")
//            .argName("file or bolt")
//            .desc("首选数据源").required()
//            .build());
        OptionGroup primaryGroupType = new OptionGroup();
        primaryGroupType.addOption(Option.builder("pN")
                .longOpt("primary-neo4j")
                .argName("bolt")
                .hasArg()
                .desc("使用neo4j作为首选数据源（默认：bolt://localhost)")
                .build());
        primaryGroupType.addOption(Option.builder("pF")
                .longOpt("primary-file")
                .argName("graph-file")
                .hasArg()
                .desc("使用文件作为首选数据源")
                .build());
        primaryGroupType.setRequired(false);
        options.addOptionGroup(primaryGroupType);

//        options.addOption(org.apache.commons.cli.Option.builder("s")
//                .longOpt("secondary-db")
//                .argName("file or bolt")
//                .desc("第二数据源").required(false)
//                .build());
        OptionGroup secondaryGroupType = new OptionGroup();
        secondaryGroupType.addOption(Option.builder("sN")
                .longOpt("secondary-neo4j")
                .desc("使用neo4j作为第二数据源")
                .argName("bolt")
                .hasArg()
                .build());
        secondaryGroupType.addOption(Option.builder("sF")
                .longOpt("secondary-file")
                .desc("使用文件作为第二数据源")
                .argName("graph-file")
                .hasArg()
                .build());
        secondaryGroupType.setRequired(false);
        options.addOptionGroup(secondaryGroupType);

        OptionGroup operationGroup = new OptionGroup();
        operationGroup.addOption(new Option("i","info",false,"查看信息"));
        operationGroup.addOption(Option.builder("eN")
                .longOpt("export-neo4j")
                .desc("输出到neo4j")
                .argName("output-bolt")
                .hasArg()
                .build());
        operationGroup.addOption(Option.builder("eF")
                .longOpt("export-file")
                .desc("输出到文件")
                .argName("output-file")
                .hasArg()
                .build());
        operationGroup.addOption(Option.builder("c")
                .longOpt("create-match-file")
                .desc("生成合并指导文件")
                .argName("output-file")
                .hasArg()
                .build());
        operationGroup.addOption(new Option("h","help",false,"打印此帮助"));
        operationGroup.setRequired(true);
        options.addOptionGroup(operationGroup);

        options.addOption(Option.builder("m")
                .longOpt("match-file")
                .desc("指定合并指导文件")
                .argName("match-file")
                .hasArg()
                .required(false)
                .build());

        Class.forName("cn.edu.hit.kg.Neo4jDataProvider");

        HelpFormatter hf = new HelpFormatter();
        hf.setWidth(110);
        CommandLineParser parser = new DefaultParser();

        try {
            CommandLine commandLine = parser.parse(options, args, true);
            if (commandLine.hasOption('h')) {
                showHelp(options, hf);
                return;
            }

            GraphData primaryDB = new GraphData();
            if (commandLine.hasOption("primary-neo4j")){
                String bolt = commandLine.getOptionValue("primary-neo4j");
                loadNeo4jDB(primaryDB, bolt);
            }else if (commandLine.hasOption("primary-file")){
                String graphPath = commandLine.getOptionValue("primary-file");
                loadGraphFile(primaryDB, graphPath);
            }else {
                loadNeo4jDB(primaryDB,"bolt://localhost");
            }

            GraphData secondaryDB = new GraphData();
            if (commandLine.hasOption("secondary-neo4j")){
                String bolt = commandLine.getOptionValue("secondary-neo4j");
                loadNeo4jDB(secondaryDB, bolt);
            }else if (commandLine.hasOption("secondary-file")){
                String graphPath = commandLine.getOptionValue("secondary-file");
                loadGraphFile(secondaryDB, graphPath);
            }

            if (commandLine.hasOption("info")){
                info(primaryDB,secondaryDB);
                return;
            }

            String matchFile = null;
            if (commandLine.hasOption("match-file")){
                matchFile = commandLine.getOptionValue("match-file");
            }

            if (commandLine.hasOption("export-neo4j")){
                String bolt = commandLine.getOptionValue("export-neo4j");
                mergeToNeo4j(primaryDB,secondaryDB,matchFile,bolt,commandLine.getArgList());
                return;
            }

            if (commandLine.hasOption("export-file")){
                String output = commandLine.getOptionValue("export-file");
                mergeToFile(primaryDB,secondaryDB,matchFile,output,commandLine.getArgList());
                return;
            }

            if (commandLine.hasOption("create-match-file")){
                String output = commandLine.getOptionValue("create-match-file");
                makeMergeFile(primaryDB,secondaryDB,output,commandLine.getArgList());
                return;
            }
            showHelp(options, hf);
        } catch (ParseException | IOException e) {
            e.printStackTrace();
            showHelp(options, hf);
        }
    }

    public static void showHelp(Options options, HelpFormatter hf) {
        hf.printHelp("java -jar data-exporter.jar", null,options,
                "可在命令最后限定导出的具体内容，以空格分隔，格式如下：\n" +
                        "  p:<node-id> s:<node-id> !p:<node-id> !s:<node-id> \n" +
                        "分别表示首选、第二数据库中导出、不导出指定节点及相关数据。",
                true);
    }

    private static void makeMergeFile(GraphData primaryDB, GraphData secondaryDB, String output, List<String> argList) {

        //TODO 先不考虑argList的筛选，不考虑结构是否相同的问题
        // 1. id相同  -->  x.完全相同   a. 类型相同，内容不同    b. 类型不同
        // 2. id不同，同类型  -->  只处理 Component、Tool、Material name相同的情况


        //TODO 输出结果到文件中，分为4个部分（交互式？all,true,false,skip）
        // 合并方式： 使用p | 使用s | 使用 用户指定的内容
        // 1. 可能相同的，判断依据
        // 2. 认为不同的，依据
        // 3. 不确定、待补充的
        // 4. 参考部分

        // 采用文件进行合并是，只会处理标记出认为一致的节点
    }

    private static void mergeToFile(GraphData primaryDB, GraphData secondaryDB, String matchFile, String output, List<String> argList) {

    }

    private static void mergeToNeo4j(GraphData primaryDB, GraphData secondaryDB, String matchFile, String bolt, List<String> argList) {

    }

    private static void info(GraphData primaryDB, GraphData secondaryDB) {
        System.out.println("======首选数据库=====");
        System.out.println("节点总数 = " + primaryDB.getNodes().size());
        System.out.println("故障总数 = " + primaryDB.getIdSetByLabel("Fault").size());
        System.out.println("======第二数据库=====");
        System.out.println("节点总数 = " + secondaryDB.getNodes().size());
        System.out.println("故障总数 = " + secondaryDB.getIdSetByLabel("Fault").size());

    }

    public static void loadGraphFile(GraphData db, String graphPath) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        App.GraphStruct provider = objectMapper.readValue(Files.newBufferedReader(
                Paths.get(graphPath)),
                App.GraphStruct.class);
        provider.nodes.forEach((id,node)->{
            db.addNode(node);
        });
//                primaryDB.getNodes().putAll(provider.nodes);
        db.setEdges(provider.edges);
        db.setLabelGroups(provider.labelGroups);
    }

    public static void loadNeo4jDB(GraphData db, String bolt) {
        Neo4jDataProvider provider = new Neo4jDataProvider();

        provider.init(bolt,"neo4j","123456");
        db.setNodes(provider.getNodes());
        db.setEdges(provider.getEdges());
        db.setLabelGroups(provider.getLabelGroups());
    }
}
